#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Muto Robot Command Executor

This module contains all command execution logic for the Muto Robot Controller.
"""

from re import S
import sys
import os
from typing import List, Dict, Any
import asyncio
import requests
import threading
import time

# 添加packages路径
packages_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../../../../../packages'))
sys.path.append(os.path.join(packages_path, 'muto_hexapod_lib'))
sys.path.append(os.path.join(packages_path, 'voice_module'))

try:
    from muto_hexapod_lib.MutoLargemodelInterface import MutoController
except ImportError as e:
    print(f"Warning: Could not import MutoController: {e}")
    MutoController = None

try:
    from voice_module.voice_interface import VoiceInterface
except ImportError as e:
    print(f"Warning: Could not import VoiceInterface: {e}")
    VoiceInterface = None

from .models import CommandPlan, VoiceType
from .memory_guard import get_memory_guard, memory_protected
import json
import traceback

# Dify服务器配置常量
DIFY_BASE_URL = "http://192.168.12.57"
DIFY_API_KEY = "app-eLGYgnsVzu1eClNm0PPYtA7o"


class MutoCommandExecutor:
    """Muto机器人命令执行器"""
    
    def __init__(self, logger=None, camera_manager=None):
        self.logger = logger
        self.camera_manager = camera_manager
        self.muto_interface = None
        self.voice_interface = None
        self.dify_client = None
        self.workflow_client = None
        # 全局取消事件（支持跨线程/跨请求打断）
        self.cancel_event = threading.Event()
        self._last_cancel_ts = 0.0
        # 会话运行状态标志：用于区分“当前正在执行的会话”与“残留的取消标志”
        self.plan_running = False

        class SessionLifecycleManager:
            def __init__(self, cancel_event: threading.Event):
                self.cancel_event = cancel_event
                self.plan_running = False
                self.last_cancel_ts = 0.0
            def is_cancel_active(self) -> bool:
                return self.cancel_event.is_set()
            def begin_session(self):
                self.plan_running = True
            def end_session(self):
                self.plan_running = False
            def request_cancel(self):
                self.cancel_event.set()
                self.last_cancel_ts = time.time()
            def clear_cancel(self):
                self.cancel_event.clear()

        self.session_manager = SessionLifecycleManager(self.cancel_event)
        
        # 初始化内存保护
        self.memory_guard = get_memory_guard(logger=self.logger)
        
        # 递归执行计数器，防止无限递归
        self.execution_depth = 0
        self.max_execution_depth = 10  # 最大递归深度
        
        # 命令序列跟踪，防止重复执行相同的命令序列
        self.command_sequence_history = []
        self.max_sequence_history = 20  # 最多保留20个历史序列
        self.duplicate_sequence_threshold = 3  # 相同序列出现3次则认为是循环
        
        # 初始化各个接口
        self.init_muto_controller()
        self.init_voice_interface()
        self.init_dify_clients()

    # 取消/恢复控制
    def request_cancel(self) -> Dict[str, Any]:
        """触发全局取消：
        - 设置取消事件
        - 立即打断语音播放/录音
        - 停止机器人当前动作/导航/学习
        """
        try:
            self.session_manager.request_cancel()
            self._last_cancel_ts = self.session_manager.last_cancel_ts
            if self.logger:
                self.logger.warning("Global cancellation requested. Signaling subsystems to stop.")

            # 语音子系统：打断播放并清空队列、停止录音
            try:
                if self.voice_interface:
                    try:
                        self.voice_interface.interrupt_audio_playback()
                    except Exception as e:
                        if self.logger:
                            self.logger.warning(f"Voice interrupt_audio_playback error: {e}")
                    try:
                        self.voice_interface.stop_audio(force=True)
                    except Exception as e:
                        if self.logger:
                            self.logger.warning(f"Voice stop_audio error: {e}")
                    try:
                        self.voice_interface.stop_recording()
                    except Exception as e:
                        if self.logger:
                            self.logger.warning(f"Voice stop_recording error: {e}")
            except Exception as e:
                if self.logger:
                    self.logger.warning(f"Voice subsystem cancellation error: {e}")

            # 机器人控制子系统：停止当前动作/导航/学习
            try:
                if self.muto_interface:
                    for fn_name, kwargs in [
                        ("stop_action", {}),
                        ("stop", {}),
                        ("cancel_navigation", {}),
                        ("stop_navigation_system", {"force_kill": True, "wait_time_s": 3.0}),
                        ("stop_action_learning", {}),
                    ]:
                        try:
                            if hasattr(self.muto_interface, fn_name):
                                fn = getattr(self.muto_interface, fn_name)
                                fn(**kwargs) if kwargs else fn()
                        except Exception as e:
                            if self.logger:
                                self.logger.warning(f"MutoInterface {fn_name} error: {e}")
            except Exception as e:
                if self.logger:
                    self.logger.warning(f"Robot subsystem cancellation error: {e}")

            return {"success": True, "message": "Cancellation requested"}
        except Exception as e:
            return {"success": False, "message": f"Failed to request cancellation: {str(e)}"}

    def clear_cancel(self) -> Dict[str, Any]:
        """清除取消状态，允许新的执行请求继续运行"""
        try:
            self.session_manager.clear_cancel()
            if self.logger:
                self.logger.info("Global cancellation cleared. Execution can resume.")

            # 同步清除机器人侧的取消标志，避免残留导致下一轮移动直接被判定为取消
            try:
                if self.muto_interface and hasattr(self.muto_interface, 'robot_controller'):
                    robot_ctrl = getattr(self.muto_interface, 'robot_controller')
                    if hasattr(robot_ctrl, 'cancel_event') and robot_ctrl.cancel_event.is_set():
                        if self.logger:
                            self.logger.info("Detected residual robot_controller.cancel_event. Clearing it via robot_controller.clear_cancel().")
                        if hasattr(robot_ctrl, 'clear_cancel'):
                            robot_ctrl.clear_cancel()
                        else:
                            # 兼容：如果没有clear_cancel方法，直接尝试复位stop状态
                            robot_ctrl.cancel_event.clear()
            except Exception as e:
                if self.logger:
                    self.logger.warning(f"Failed to clear robot-side cancellation flag: {e}")
            return {"success": True, "message": "Cancellation cleared"}
        except Exception as e:
            return {"success": False, "message": f"Failed to clear cancellation: {str(e)}"}
    
    def init_muto_controller(self):
        """初始化Muto控制器接口"""
        max_retries = 3
        retry_count = 0
        
        while retry_count < max_retries:
            try:
                if MutoController:
                    self.muto_interface = MutoController()
                    if self.logger:
                        self.logger.info('MutoController initialized successfully')
                    return
                else:
                    if self.logger:
                        self.logger.warn('MutoController not available, using mock interface')
                    self.muto_interface = None
                    return
            except Exception as e:
                retry_count += 1
                if self.logger:
                    self.logger.error(f'Failed to initialize MutoController (attempt {retry_count}/{max_retries}): {e}')
                
                if retry_count >= max_retries:
                    if self.logger:
                        self.logger.error('MutoController initialization failed after all retries')
                    self.muto_interface = None
                    return
                else:
                    # 短暂等待后重试
                    import time
                    time.sleep(1.0)
    
    def init_voice_interface(self):
        """初始化语音接口"""
        try:
            if VoiceInterface:
                # 获取项目根目录的绝对路径
                import os
                # 从ROS2包目录向上找到项目根目录
                current_dir = os.path.dirname(os.path.abspath(__file__))
                # 向上查找到包含packages目录的根目录
                project_root = current_dir
                while project_root != '/' and not os.path.exists(os.path.join(project_root, 'packages')):
                    project_root = os.path.dirname(project_root)
                
                # 构建配置文件路径
                config_path = os.path.join(project_root, 'packages', 'voice_module', 'voice_module', 'config', 'voice_config.yaml')
                
                self.voice_interface = VoiceInterface(config_path=config_path)
                # 手动设置config_path属性以供后续使用
                self.voice_interface.config_path = config_path
                # 初始化所有必要的模块，包括TTS
                success, message, _ = self.voice_interface.initialize_all()
                if success:
                    if self.logger:
                        self.logger.info('VoiceInterface initialized successfully')
                else:
                    if self.logger:
                        self.logger.warn(f'VoiceInterface initialization failed: {message}')
                    # 即使部分模块初始化失败，也保留接口以便使用可用功能
                    if self.logger:
                        self.logger.info('Keeping VoiceInterface for available functionality')
            else:
                if self.logger:
                    self.logger.warn('VoiceInterface not available')
                self.voice_interface = None
        except Exception as e:
            if self.logger:
                self.logger.error(f'Failed to initialize VoiceInterface: {e}')
            self.voice_interface = None
    
    def init_dify_clients(self):
        """初始化Dify客户端"""
        pass
    
    def resolve_dependencies(self, plan: List[CommandPlan]) -> List[CommandPlan]:
        """按照id数字顺序排序指令"""
        # 按照id的数字顺序排序
        try:
            sorted_plan = sorted(plan, key=lambda x: int(x.id))
            return sorted_plan
        except ValueError:
            # 如果id不是纯数字，按原顺序返回
            if self.logger:
                self.logger.warn("Command IDs are not numeric, using original order")
            return plan
    
    async def execute_command(self, plan_item: CommandPlan) -> Dict[str, Any]:
        """执行单个指令"""
        max_retries = 2
        retry_count = 0
        
        while retry_count <= max_retries:
            try:
                # 高优先级取消检查：在任何执行前立即返回
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning(f"Cancel flag detected before executing command {plan_item.id}, aborting")
                    return {
                        'success': False,
                        'error': 'Execution interrupted by cancel event'
                    }
                if not self.muto_interface:
                    return {
                        'success': False,
                        'error': 'Muto controller interface not available'
                    }
            
                # 解析指令格式：函数名(参数1, 参数2) 或 函数名(key=value, key2=value2)
                # 也支持 robot.函数名() 或 controller.函数名() 格式
                command_str = plan_item.command.strip()
                
                # 简单的指令解析
                if '(' in command_str and command_str.endswith(')'):
                    func_name = command_str.split('(')[0].strip()
                    
                    # 处理带前缀的函数名（如 robot.start_camera 或 controller.start_camera）
                    if '.' in func_name:
                        func_name = func_name.split('.')[-1]  # 取最后一部分作为实际函数名
                    
                    params_str = command_str[command_str.find('(')+1:-1].strip()
                    
                    # 解析参数（支持位置参数和命名参数）
                    params = []
                    kwargs = {}
                    if params_str:
                        # 分割参数，处理引号内的逗号
                        param_parts = self._split_parameters(params_str)
                        
                        for param in param_parts:
                            param = param.strip()
                            # 如果整个参数是被引号包裹的字符串，则优先作为位置参数处理，避免其中的'='被误判为命名参数
                            is_quoted_string = (
                                (param.startswith('"') and param.endswith('"')) or
                                (param.startswith("'") and param.endswith("'"))
                            )
                            if ('=' in param) and (not is_quoted_string):
                                # 命名参数（只在不被完整引号包裹时识别为命名参数）
                                key, value = param.split('=', 1)
                                key = key.strip()
                                value = value.strip().strip('"').strip("'")
                                # 类型转换
                                kwargs[key] = self._convert_parameter_value(value)
                            else:
                                # 位置参数
                                param = param.strip('"').strip("'")
                                params.append(self._convert_parameter_value(param))
                    
                    # 检查是否是特殊命令
                    if func_name == 'robot_speak':
                        # 机器人语音播放 - 文字转语音并非阻塞式播放
                        text = None
                        volume = None
                        
                        if kwargs:
                            text = kwargs.get('text')
                            volume = kwargs.get('volume')
                        elif params:
                            text = params[0] if len(params) > 0 else None
                            volume = params[1] if len(params) > 1 else None
                        
                        if not text:
                            return {'success': False, 'error': 'robot_speak requires text parameter'}
                        
                        # 使用CUSTOM_TEXT类型非阻塞播放，支持并行执行
                        from .models import VoiceType
                        # 高优先级取消检查：避免开始语音播放
                        if self.cancel_event.is_set():
                            if self.logger:
                                self.logger.warning("Cancel flag detected before robot_speak, skipping")
                            return {
                                'success': False,
                                'error': 'Execution interrupted by cancel event'
                            }
                        result = self.play_voice(VoiceType.CUSTOM_TEXT, text, volume)
                        return result
                    
                    elif func_name == 'have_a_look':
                        # 视觉感知命令 - 拍照、上传图片、执行工作流获取新指令序列
                        user_query = None
                        
                        if kwargs:
                            user_query = kwargs.get('user_query') or kwargs.get('query') or kwargs.get('question') or kwargs.get('description')
                        elif params:
                            user_query = params[0] if len(params) > 0 else None
                        
                        # 如果没有提供user_query，自动生成包含执行上下文的查询
                        if not user_query:
                            # 构建执行上下文
                            context_parts = []
                            
                            # 添加原始用户问题
                            if hasattr(plan_item, 'original_user_query') and plan_item.original_user_query:
                                context_parts.append(f"用户原始问题: {plan_item.original_user_query}")
                            
                            # 添加执行历史
                            if hasattr(plan_item, 'execution_results') and plan_item.execution_results:
                                successful_actions = []
                                for result in plan_item.execution_results:
                                    if result.get('success', False):
                                        cmd_desc = result.get('command_description', '未知操作')
                                        successful_actions.append(cmd_desc)
                                
                                if successful_actions:
                                    context_parts.append(f"已完成的操作: {', '.join(successful_actions)}")
                            
                            # 生成默认查询
                            if context_parts:
                                user_query = f"请观察当前环境状态。{'; '.join(context_parts)}。请根据这些信息分析当前情况并决定下一步行动。"
                            else:
                                user_query = "请观察当前环境状态，分析情况并决定下一步行动。"
                            
                        if self.logger:
                            self.logger.info(f"Auto-generated user_query for have_a_look: {user_query}")
                        
                        # 执行视觉感知流程
                        # 高优先级取消检查：避免进入视觉流程
                        if self.cancel_event.is_set():
                            if self.logger:
                                self.logger.warning("Cancel flag detected before have_a_look execution, skipping")
                            return {
                                'success': False,
                                'error': 'Execution interrupted by cancel event'
                            }
                        result = self._execute_have_a_look(plan_item.id, user_query)
                        # have_a_look命令必须返回结果，否则会被当作未知函数处理
                        return result
                    
                    # 处理特殊的wait_for_next_step指令
                    elif func_name == "wait_for_next_step":
                        # 提取参数（兼容位置参数与命名参数；仅要求next_planning必填）
                        original_request = None
                        executed_commands = None
                        next_planning = None

                        # 先尝试从命名参数获取（支持多种别名）
                        if kwargs:
                            original_request = (
                                kwargs.get('original_request')
                                or kwargs.get('user_query')
                                or kwargs.get('request')
                                or kwargs.get('title')
                            )
                            executed_commands = (
                                kwargs.get('executed_commands')
                                or kwargs.get('execution_context')
                                or kwargs.get('executed')
                                or kwargs.get('executed_commands_summary')
                            )
                            next_planning = (
                                kwargs.get('next_planning')
                                or kwargs.get('planning')
                                or kwargs.get('next')
                                or kwargs.get('next_instructions')
                            )

                        # 位置参数回退逻辑：
                        # - 若仅提供一个位置参数，则视为next_planning
                        # - 若提供三个位置参数，则依次映射为original_request、executed_commands、next_planning
                        if params:
                            if len(params) == 1 and not next_planning:
                                next_planning = params[0]
                            elif len(params) >= 3:
                                original_request = original_request or params[0]
                                executed_commands = executed_commands or params[1]
                                next_planning = next_planning or params[2]

                        # 如果仍有关键参数缺失，尝试使用当前执行历史填充executed_commands
                        current_execution_results = getattr(self, '_current_execution_results', None)
                        if not executed_commands and current_execution_results:
                            try:
                                executed_commands = self._format_execution_context(current_execution_results)
                            except Exception:
                                # 保底：不影响后续逻辑
                                pass

                        # 最终校验：仅要求next_planning存在
                        if not next_planning:
                            return {
                                'success': False,
                                'error': (
                                    "wait_for_next_step requires 'next_planning' (original_request and executed_commands are optional). "
                                    f"Got next_planning={repr(next_planning)}, "
                                    f"kwargs_keys={list(kwargs.keys()) if kwargs else []}, params_count={(len(params) if params else 0)}"
                                )
                            }

                        # 执行等待下一步规划流程
                        # 高优先级取消检查：避免进入等待流程
                        if self.cancel_event.is_set():
                            if self.logger:
                                self.logger.warning("Cancel flag detected before wait_for_next_step execution, skipping")
                            return {
                                'success': False,
                                'error': 'Execution interrupted by cancel event'
                            }
                        result = self._execute_wait_for_next_step(
                            plan_item.id,
                            original_request,
                            executed_commands,
                            next_planning,
                            current_execution_results
                        )
                        return result
                    
                    
                    # 直接调用MutoController的方法，不需要别名映射
                    elif hasattr(self.muto_interface, func_name):
                        method = getattr(self.muto_interface, func_name)
                        
                        # 高优先级取消检查：避免进入控制器耗时操作
                        if self.cancel_event.is_set():
                            if self.logger:
                                self.logger.warning(f"Cancel flag detected before calling MutoController.{func_name}, skipping")
                            return {
                                'success': False,
                                'error': 'Execution interrupted by cancel event'
                            }
                        # 调用方法
                        if kwargs:
                            # 使用命名参数调用
                            result = method(**kwargs)
                        elif params:
                            result = method(*params)
                        else:
                            result = method()
                        
                        # 检查MutoController方法的返回值格式
                        # MutoController方法返回tuple: (success: bool, message: str) 或 (success: bool, message: str, data: dict)
                        # 对于雷达数据等大数据量命令，只记录摘要信息
                        if func_name in ['get_lidar_data', 'get_lidar_360_data', 'get_lidar_range_at_angle']:
                            print(f"[DEBUG] execute_command: {func_name} completed, type: {type(result)}")
                        else:
                            print(f"[DEBUG] execute_command: {func_name} returned: {result}, type: {type(result)}")
                        
                        if isinstance(result, tuple) and len(result) >= 2 and isinstance(result[0], bool):
                            method_success = result[0]
                            method_message = result[1]
                            method_data = result[2] if len(result) > 2 else None
                            
                            # 对于雷达数据，只记录关键信息
                            if func_name in ['get_lidar_data', 'get_lidar_360_data', 'get_lidar_range_at_angle'] and method_data:
                                data_summary = {}
                                if isinstance(method_data, dict):
                                    # 只保留关键字段，过滤掉大数组
                                    for key, value in method_data.items():
                                        if key in ['ranges', 'sample_points', 'angle_data', 'distance_data', 'intensity_data']:
                                            if isinstance(value, list):
                                                data_summary[key] = f"[{len(value)} items]"
                                            else:
                                                data_summary[key] = value
                                        elif key not in ['ranges_data', 'intensities']:  # 跳过其他大数组
                                            data_summary[key] = value
                                print(f"[DEBUG] execute_command: {func_name} - success={method_success}, data_summary={data_summary}")
                            else:
                                print(f"[DEBUG] execute_command: parsed result - success={method_success}, message={method_message}, data={method_data}")
                            
                            if method_success:
                                response = {'success': True, 'message': method_message}
                                if method_data is not None:
                                    response['data'] = method_data
                                # 对于雷达数据，不记录完整响应
                                if func_name in ['get_lidar_data', 'get_lidar_360_data', 'get_lidar_range_at_angle']:
                                    print(f"[DEBUG] execute_command: {func_name} response prepared successfully")
                                else:
                                    print(f"[DEBUG] execute_command: final response = {response}")
                                return response
                            else:
                                error_msg = f"Method {func_name} failed: {method_message}"
                                print(f"[ERROR] {error_msg}")
                                print(f"[ERROR] Method result: {result}")
                                return {
                                    'success': False,
                                    'error': error_msg
                                }
                        else:
                            # 如果返回值格式不符合预期，仍然返回成功
                            return {'success': True}
                    else:
                        return {
                            'success': False,
                            'error': f'Unknown function: {func_name}. Available functions: {[attr for attr in dir(self.muto_interface) if not attr.startswith("_")]}'
                        }
                else:
                    return {
                        'success': False,
                        'error': f'Invalid command format: {command_str}'
                    }
                    
            except Exception as e:
                retry_count += 1
                error_msg = f'Exception executing command (attempt {retry_count}/{max_retries + 1}): {str(e)}'
                
                if self.logger:
                    self.logger.error(error_msg)
                    # 输出堆栈，便于定位失败原因
                    try:
                        self.logger.error(f"Stack trace:\n{traceback.format_exc()}")
                    except Exception:
                        pass
                
                # 添加控制台输出
                print(f"[ERROR] {error_msg}")
                print(f"[ERROR] Command: {plan_item.command}")
                print(f"[ERROR] Exception type: {type(e).__name__}")
                try:
                    print(traceback.format_exc())
                except Exception:
                    pass
                
                if retry_count > max_retries:
                    final_error = f'Command failed after {max_retries + 1} attempts: {str(e)}'
                    print(f"[ERROR] {final_error}")
                    return {
                        'success': False,
                        'error': final_error
                    }
                else:
                    # 短暂等待后重试
                    if self.logger:
                        self.logger.info(f'Retrying command in 0.5 seconds... ({retry_count}/{max_retries + 1})')
                    print(f"[INFO] Retrying command in 0.5 seconds... ({retry_count}/{max_retries + 1})")
                    import asyncio
                    await asyncio.sleep(0.5)
        
        # 如果所有重试都失败，返回最后的错误
        return {
            'success': False,
            'error': 'Command execution failed after all retries'
        }
    
    @memory_protected(func_name="execute_plan", max_memory_percent=95.0)
    async def execute_plan(self, plan_data: Dict[str, Any], original_user_query: str = None) -> Dict[str, Any]:
        """执行指令序列 - 严格按ID顺序执行，支持robot_speak特殊并行处理，带内存保护和递归深度限制"""
        
        # 检查递归深度
        self.execution_depth += 1
        if self.execution_depth > self.max_execution_depth:
            error_msg = f"Maximum execution depth ({self.max_execution_depth}) exceeded. Possible infinite recursion detected."
            if self.logger:
                self.logger.error(error_msg)
            self.execution_depth -= 1
            return {
                'success': False,
                'message': error_msg,
                'executed_commands': [],
                'failed_commands': []
            }
        
        try:
            # 检查内存状态
            memory_status = self.memory_guard.check_memory_status()
            if memory_status['status'] == 'critical':
                if self.logger:
                    self.logger.warning(f"Critical memory status at execution depth {self.execution_depth}")
                # 执行紧急清理
                self.memory_guard.emergency_cleanup()
            
            # 记录执行开始
            if self.logger:
                self.logger.info(f"Starting plan execution at depth {self.execution_depth}")
                self.logger.info(f"Memory usage: {memory_status['memory_info'].get('memory_usage_percent', 'unknown')}%")
            # 在最外层开始时，设置会话运行标志，并自动清除可能的“残留取消标志”
            if self.execution_depth == 1:
                # 如果发现取消事件已设置但当前并无会话在运行，视为残留标志，自动清除
                if self.session_manager.is_cancel_active() and not self.session_manager.plan_running:
                    if self.logger:
                        self.logger.info("Residual cancel flag detected before starting new session. Auto-clearing it.")
                    self.session_manager.clear_cancel()
                # 同步清除机器人侧的残留取消标志（如果上一轮取消后未恢复）
                try:
                    if self.muto_interface and hasattr(self.muto_interface, 'robot_controller'):
                        robot_ctrl = getattr(self.muto_interface, 'robot_controller')
                        if hasattr(robot_ctrl, 'cancel_event') and robot_ctrl.cancel_event.is_set():
                            if self.logger:
                                self.logger.info("Residual robot_controller.cancel_event detected at session start. Auto-clearing it.")
                            if hasattr(robot_ctrl, 'clear_cancel'):
                                robot_ctrl.clear_cancel()
                            else:
                                robot_ctrl.cancel_event.clear()
                except Exception as e:
                    if self.logger:
                        self.logger.warning(f"Failed to auto-clear robot-side cancellation at session start: {e}")
                # 标记当前会话为运行中
                self.session_manager.begin_session()
                self.plan_running = True
        except Exception as e:
            error_msg = f"Error in execute_plan: {e}"
            if self.logger:
                self.logger.error(error_msg)
        
        try:
            # 检查计划状态
            if plan_data.get('status') != 'success':
                return {
                    'success': False,
                    'message': f'Plan status is not success: {plan_data.get("status")}',
                    'results': []
                }
            
            # 获取计划列表
            plan_list = plan_data.get('plan', [])
            if not plan_list:
                return {
                    'success': False,
                    'message': 'No plan items found',
                    'results': []
                }
            
            # 生成当前命令序列的签名，用于检测循环
            command_signature = self._generate_command_signature(plan_list)
            
            # 检查是否存在重复的命令序列（防止循环）
            if self._is_duplicate_sequence(command_signature):
                warning_msg = f"Detected potential command loop at depth {self.execution_depth}. Sequence signature: {command_signature[:50]}..."
                if self.logger:
                    self.logger.warning(warning_msg)
                # 不直接返回错误，而是继续执行但记录警告
                # 这样可以避免误判，但仍然提供循环检测信息
            
            # 记录当前命令序列
            self._record_command_sequence(command_signature)
            
            # 转换为CommandPlan对象
            command_plans = []
            for item in plan_list:
                try:
                    command_plan = CommandPlan(**item)
                    command_plans.append(command_plan)
                except Exception as e:
                    self.logger.error(f"Error creating CommandPlan from {item}: {e}")
                    return {
                        'success': False,
                        'message': f'Invalid plan item: {e}',
                        'results': []
                    }
            
            # 解析依赖关系并排序（严格按ID顺序）
            sorted_plans = self.resolve_dependencies(command_plans)
            
            # 执行指令序列 - 新的执行逻辑
            results = []
            failed_commands = set()
            completed_commands = set()
            robot_speak_task = None  # 当前运行的robot_speak任务
            robot_speak_cmd_id = None  # 当前robot_speak命令的ID
            session_interrupted = False  # 标记本次会话是否被中途中断
            
            # 设置当前执行结果到实例变量，供wait_for_next_step使用
            self._current_execution_results = results
            
            # 严格按ID顺序处理每个指令
            i = 0
            while i < len(sorted_plans):
                # 每轮检查全局取消
                if self.session_manager.is_cancel_active():
                    if self.logger:
                        self.logger.warning("Cancellation flag detected during plan execution loop. Aborting remaining commands.")
                    # 标记为中途中断，并最终汇总返回（保留已执行的结果）
                    session_interrupted = True
                    break
                plan_item = sorted_plans[i]
                
                # 检查依赖关系
                if any(dep in failed_commands for dep in plan_item.dependencies):
                    failed_deps = [dep for dep in plan_item.dependencies if dep in failed_commands]
                    error_msg = f"Skipping command {plan_item.id} due to failed dependencies: {failed_deps}"
                    self.logger.warning(error_msg)
                    print(f"[ERROR] {error_msg}")  # 添加控制台输出
                    failed_commands.add(plan_item.id)
                    results.append({
                        'id': plan_item.id,
                        'command': plan_item.command,
                        'description': plan_item.description,
                        'critical': plan_item.critical,
                        'result': {
                            'success': False,
                            'error': error_msg
                        }
                    })
                    i += 1
                    continue
                
                # 检查是否是robot_speak指令
                if plan_item.command.startswith('robot_speak'):
                    # 如果有之前的robot_speak任务在运行，先等待其完成
                    if robot_speak_task and not robot_speak_task.done():
                        self.logger.info(f"Waiting for previous robot_speak task {robot_speak_cmd_id} to complete")
                        await robot_speak_task
                        robot_speak_task = None
                        robot_speak_cmd_id = None
                    
                    # 启动新的robot_speak任务（非阻塞）
                    self.logger.info(f"Starting robot_speak command {plan_item.id}: {plan_item.command}")
                    # 如果已取消则跳过启动robot_speak
                    if self.session_manager.is_cancel_active():
                        results.append({
                            'id': plan_item.id,
                            'command': plan_item.command,
                            'description': plan_item.description,
                            'critical': plan_item.critical,
                            'result': {
                                'success': False,
                                'error': 'Execution interrupted before starting robot_speak'
                            }
                        })
                        failed_commands.add(plan_item.id)
                        i += 1
                        continue

                    robot_speak_task = asyncio.create_task(self.execute_command(plan_item))
                    robot_speak_cmd_id = plan_item.id
                    
                    # 立即标记为已开始（非阻塞特性）
                    results.append({
                        'id': plan_item.id,
                        'command': plan_item.command,
                        'description': plan_item.description,
                        'critical': plan_item.critical,
                        'result': {
                            'success': True,
                            'message': 'Robot speech started (non-blocking)',
                            'command_id': plan_item.id,
                            'command': plan_item.command
                        }
                    })
                    completed_commands.add(plan_item.id)
                    
                    # 检查是否有parallel_group，如果有，寻找同组的其他指令
                    if hasattr(plan_item, 'parallel_group') and plan_item.parallel_group:
                        parallel_group = plan_item.parallel_group
                        self.logger.info(f"Robot_speak {plan_item.id} has parallel_group: {parallel_group}")
                        
                        # 寻找同组的后续指令并并行执行
                        parallel_tasks = []
                        j = i + 1
                        while j < len(sorted_plans):
                            next_cmd = sorted_plans[j]
                            if (hasattr(next_cmd, 'parallel_group') and 
                                next_cmd.parallel_group == parallel_group):
                                
                                # 检查sync_with依赖
                                if (hasattr(next_cmd, 'sync_with') and next_cmd.sync_with and 
                                    plan_item.id in next_cmd.sync_with):
                                    # 等待robot_speak开始播放后再执行
                                    await asyncio.sleep(0.5)  # 短暂延迟确保语音开始播放
                                    self.logger.info(f"Command {next_cmd.id} syncing with robot_speak {plan_item.id}")
                                
                                # 启动并行任务
                                self.logger.info(f"Starting parallel command {next_cmd.id}: {next_cmd.command}")
                                task = asyncio.create_task(self.execute_command(next_cmd))
                                parallel_tasks.append((next_cmd, task))
                                j += 1
                            else:
                                break
                        
                        # 等待所有并行任务完成
                        for cmd, task in parallel_tasks:
                            result = await task
                            results.append({
                                'id': cmd.id,
                                'command': cmd.command,
                                'description': cmd.description,
                                'critical': cmd.critical,
                                'result': result
                            })
                            
                            if not result.get('success', False):
                                failed_commands.add(cmd.id)
                            else:
                                completed_commands.add(cmd.id)
                        
                        # 等待robot_speak任务完成
                        if robot_speak_task and not robot_speak_task.done():
                            robot_speak_result = await robot_speak_task
                            # 更新robot_speak的结果
                            for result_item in results:
                                if result_item['id'] == plan_item.id:
                                    result_item['result'] = robot_speak_result
                                    break
                            
                            if not robot_speak_result.get('success', False):
                                failed_commands.add(plan_item.id)
                                completed_commands.discard(plan_item.id)
                        
                        robot_speak_task = None
                        robot_speak_cmd_id = None
                        
                        # 跳过已处理的并行指令
                        i = j
                        continue
                
                else:
                    # 非robot_speak指令的处理
                    # 如果有robot_speak任务在运行且当前指令不在同一并行组，等待robot_speak完成
                    if robot_speak_task and not robot_speak_task.done():
                        # 检查是否在同一并行组
                        in_same_group = False
                        if hasattr(plan_item, 'parallel_group') and plan_item.parallel_group:
                            # 查找对应的robot_speak指令
                            for prev_cmd in sorted_plans[:i]:
                                if (prev_cmd.id == robot_speak_cmd_id and 
                                    hasattr(prev_cmd, 'parallel_group') and 
                                    prev_cmd.parallel_group == plan_item.parallel_group):
                                    in_same_group = True
                                    break
                        
                        if not in_same_group:
                            self.logger.info(f"Waiting for robot_speak task {robot_speak_cmd_id} to complete before executing {plan_item.id}")
                            robot_speak_result = await robot_speak_task
                            # 更新robot_speak的结果
                            for result_item in results:
                                if result_item['id'] == robot_speak_cmd_id:
                                    result_item['result'] = robot_speak_result
                                    break
                            
                            if not robot_speak_result.get('success', False):
                                failed_commands.add(robot_speak_cmd_id)
                                completed_commands.discard(robot_speak_cmd_id)
                            
                            robot_speak_task = None
                            robot_speak_cmd_id = None
                    
                    # 执行当前指令
                    self.logger.info(f"Executing command {plan_item.id}: {plan_item.command}")
                    # 取消检查：在调用前尽量避免进入长阻塞
                    if self.session_manager.is_cancel_active():
                        results.append({
                            'id': plan_item.id,
                            'command': plan_item.command,
                            'description': plan_item.description,
                            'critical': plan_item.critical,
                            'result': {
                                'success': False,
                                'error': 'Execution interrupted before command call'
                            }
                        })
                        failed_commands.add(plan_item.id)
                        i += 1
                        continue

                    result = await self.execute_command(plan_item)
                    
                    results.append({
                        'id': plan_item.id,
                        'command': plan_item.command,
                        'description': plan_item.description,
                        'critical': plan_item.critical,
                        'result': result
                    })
                    
                    if not result.get('success', False):
                        error_msg = f"Command {plan_item.id} failed: {result.get('error', 'Unknown error')}"
                        self.logger.error(error_msg)
                        print(f"[ERROR] {error_msg}")  # 添加控制台输出
                        print(f"[ERROR] Command details: {plan_item.command}")  # 显示失败的命令
                        print(f"[ERROR] Full result: {result}")  # 显示完整的结果信息
                        failed_commands.add(plan_item.id)
                    else:
                        completed_commands.add(plan_item.id)
                        
                        # 检查是否有后续指令需要执行
                        # 只有当指令明确返回fetch_next_commands=True时才尝试获取后续指令
                        if result.get('fetch_next_commands') or result.get('next_commands'):
                            if result.get('next_commands'):
                                next_commands = result['next_commands']
                            else:
                                next_commands_result = await self.fetch_next_commands(
                                    plan_item.id,
                                    self._format_execution_context(results)
                                )
                                next_commands = next_commands_result.get('plan', []) if next_commands_result.get('success') else []
                        else:
                            next_commands = []
                        
                        # 递归执行后续指令
                        if next_commands and not self.session_manager.is_cancel_active():
                            self.logger.info(f"Executing {len(next_commands)} next commands for {plan_item.id}")
                            next_result = await self.execute_plan({
                                'status': 'success',
                                'plan': next_commands
                            }, original_user_query)
                            
                            if next_result.get('success'):
                                # 修复键名错误：使用 'executed_commands' 而不是 'results'
                                next_executed_commands = next_result.get('executed_commands', [])
                                results.extend(next_executed_commands)
                                self.logger.info(f"Successfully merged {len(next_executed_commands)} next commands results")
                                print(f"[DEBUG] Merged {len(next_executed_commands)} next commands results for {plan_item.id}")
                            else:
                                error_msg = f"Failed to execute next commands for {plan_item.id}: {next_result.get('message')}"
                                self.logger.error(error_msg)
                                print(f"[ERROR] {error_msg}")  # 添加控制台输出
                                print(f"[ERROR] Next commands result: {next_result}")  # 显示详细错误信息
                                
                                # 将递归执行的失败传播到当前层级
                                failed_commands.add(plan_item.id)
                                completed_commands.discard(plan_item.id)
                                
                                # 合并递归执行的失败命令和执行结果
                                next_executed_commands = next_result.get('executed_commands', [])
                                next_failed_commands = next_result.get('failed_commands', [])
                                
                                # 添加递归执行的结果到当前结果集
                                results.extend(next_executed_commands)
                                
                                # 将递归执行中失败的命令ID添加到当前失败集合
                                if isinstance(next_failed_commands, list):
                                    for failed_cmd in next_failed_commands:
                                        if isinstance(failed_cmd, dict) and 'command_id' in failed_cmd:
                                            failed_commands.add(failed_cmd['command_id'])
                                        elif isinstance(failed_cmd, str):
                                            failed_commands.add(failed_cmd)
                                
                                print(f"[ERROR] Propagated {len(next_failed_commands)} failed commands from recursive execution")
                
                i += 1
            
            # 确保最后的robot_speak任务也完成
            if robot_speak_task and not robot_speak_task.done():
                self.logger.info(f"Waiting for final robot_speak task {robot_speak_cmd_id} to complete")
                robot_speak_result = await robot_speak_task
                # 更新robot_speak的结果
                for result_item in results:
                    if result_item['id'] == robot_speak_cmd_id:
                        result_item['result'] = robot_speak_result
                        break
                
                if not robot_speak_result.get('success', False):
                    failed_commands.add(robot_speak_cmd_id)
                    completed_commands.discard(robot_speak_cmd_id)
            
            # 检查是否有关键指令失败
            critical_failed = any(
                plan_item.critical and plan_item.id in failed_commands 
                for plan_item in sorted_plans
            )
            
            # 根据need_broadcast标志决定是否播放系统完成语音
            need_broadcast = plan_data.get('need_broadcast', False)
            if need_broadcast and not self.session_manager.is_cancel_active() and not session_interrupted:
                try:
                    # 给robot_speak任务额外的时间完成
                    await asyncio.sleep(0.5)
                    self.play_voice(VoiceType.SYSTEM_SOUND, "sentence_finish_plans")
                    self.logger.info("Completion voice played successfully (broadcast enabled)")
                except Exception as e:
                    if self.logger:
                        self.logger.warning(f"Failed to play completion voice: {str(e)}")
            else:
                self.logger.info("Skipping completion voice (broadcast disabled or interrupted)")
            
            # 检查执行结果
            total_commands = len(results)  # 实际已执行的命令数量
            failed_count = len(failed_commands)
            success_count = total_commands - failed_count
            planned_total = len(sorted_plans)  # 计划中的总命令数量
            executed_count = len(completed_commands)  # 完成执行的命令数量
            unexecuted_count = max(planned_total - executed_count, 0)
            
            # 输出执行摘要
            if failed_count > 0 or self.session_manager.is_cancel_active():
                print(f"[EXECUTION SUMMARY] Total: {total_commands}, Success: {success_count}, Failed: {failed_count}")
                print(f"[FAILED COMMANDS] {list(failed_commands)}")
                if self.session_manager.is_cancel_active():
                    print("[EXECUTION SUMMARY] Execution interrupted by cancel event")
                
                # 输出每个失败命令的详细信息
                for result_item in results:
                    if result_item['id'] in failed_commands:
                        print(f"[FAILED DETAIL] Command {result_item['id']}: {result_item['command']}")
                        print(f"[FAILED DETAIL] Error: {result_item['result'].get('error', 'Unknown error')}")
            else:
                print(f"[EXECUTION SUMMARY] All {total_commands} commands executed successfully")
                
            # 输出所有执行的命令详情（用于调试）
            print(f"[DEBUG] Final results count: {len(results)}")
            for i, result_item in enumerate(results):
                print(f"[DEBUG] Command {i+1}: ID={result_item['id']}, Success={result_item['result'].get('success', False)}")
                print(f"[DEBUG] Command {i+1} Details: {result_item['command']}")
                if result_item['result'].get('data'):
                    print(f"[DEBUG] Command {i+1} Data Keys: {list(result_item['result']['data'].keys()) if isinstance(result_item['result']['data'], dict) else 'Not a dict'}")
            
            
            if session_interrupted:
                # 中途中断：即使没有失败命令，也返回非成功并标注中断信息
                interrupted_msg = (
                    f"执行被中途中断：已执行 {executed_count}/{planned_total} 条，"
                    f"剩余 {unexecuted_count} 条未执行"
                )
                print(f"[EXECUTION SUMMARY] {interrupted_msg}")
                return {
                    'success': False,
                    'message': interrupted_msg,
                    'executed_commands': results,
                    'failed_command_id': list(failed_commands)[0] if failed_commands else None,
                    'failed_commands': [
                        {
                            'command_id': result['id'],
                            'command': result['command'],
                            'error': result['result'].get('error', 'Unknown error')
                        }
                        for result in results if result['id'] in failed_commands
                    ],
                    'error_details': [
                        {
                            'command_id': result['id'],
                            'command': result['command'],
                            'error': result['result'].get('error', 'Unknown error')
                        }
                        for result in results if result['id'] in failed_commands
                    ]
                }
            elif failed_count == 0:
                # 所有命令都成功
                return {
                    'success': True,
                    'message': f"Successfully executed {total_commands} commands",
                    'executed_commands': results
                }
            elif success_count > 0:
                # 部分成功
                error_summary = f"Partially executed {success_count}/{total_commands} commands, {failed_count} failed"
                print(f"[ERROR] {error_summary}")
                return {
                    'success': False,
                    'message': error_summary,
                    'executed_commands': results,
                    'failed_command_id': list(failed_commands)[0] if failed_commands else None,
                    'failed_commands': [
                        {
                            'command_id': result['id'],
                            'command': result['command'],
                            'error': result['result'].get('error', 'Unknown error')
                        }
                        for result in results if result['id'] in failed_commands
                    ],
                    'error_details': [
                        {
                            'command_id': result['id'],
                            'command': result['command'],
                            'error': result['result'].get('error', 'Unknown error')
                        }
                        for result in results if result['id'] in failed_commands
                    ]
                }
            else:
                # 全部失败
                error_summary = f"All {total_commands} commands failed"
                print(f"[ERROR] {error_summary}")
                return {
                    'success': False,
                    'message': error_summary,
                    'executed_commands': results,
                    'failed_command_id': list(failed_commands)[0] if failed_commands else None,
                    'failed_commands': [
                        {
                            'command_id': result['id'],
                            'command': result['command'],
                            'error': result['result'].get('error', 'Unknown error')
                        }
                        for result in results if result['id'] in failed_commands
                    ],
                    'error_details': [
                        {
                            'command_id': result['id'],
                            'command': result['command'],
                            'error': result['result'].get('error', 'Unknown error')
                        }
                        for result in results if result['id'] in failed_commands
                    ]
                }
            
        except Exception as e:
            # 方案D：在异常返回中也携带已收集的执行结果，便于路由层或上层调用方查看已执行的命令
            self.logger.error(f"Exception in execute_plan: {str(e)}")
            executed_commands = results if 'results' in locals() else []
            failed_commands_list = [
                {
                    'command_id': r['id'],
                    'command': r['command'],
                    'error': r['result'].get('error', 'Unknown error')
                }
                for r in executed_commands if not r['result'].get('success', False)
            ] if executed_commands else []

            return {
                'success': False,
                'message': f"Exception executing plan: {str(e)}",
                'executed_commands': executed_commands,
                'failed_commands': failed_commands_list,
                'error_details': failed_commands_list,
                'error': f"Exception executing plan: {str(e)}"
            }
        finally:
            # 递归深度计数器递减
            self.execution_depth -= 1
            # 如果返回到最外层，自动清理取消标志与会话状态
            if self.execution_depth == 0:
                # 标记会话结束
                self.session_manager.end_session()
                self.plan_running = False
                # 自动清除取消标志，避免影响后续新请求
                if self.session_manager.is_cancel_active():
                    try:
                        self.session_manager.clear_cancel()
                        if self.logger:
                            self.logger.info("Auto-cleared cancellation flag at end of session.")
                    except Exception:
                        pass
                # 会话结束时也尝试清除机器人侧的取消标志，避免残留影响下一轮
                try:
                    if self.muto_interface and hasattr(self.muto_interface, 'robot_controller'):
                        robot_ctrl = getattr(self.muto_interface, 'robot_controller')
                        if hasattr(robot_ctrl, 'cancel_event') and robot_ctrl.cancel_event.is_set():
                            if self.logger:
                                self.logger.info("Auto-clearing residual robot_controller.cancel_event at end of session.")
                            if hasattr(robot_ctrl, 'clear_cancel'):
                                robot_ctrl.clear_cancel()
                            else:
                                robot_ctrl.cancel_event.clear()
                except Exception as e:
                    if self.logger:
                        self.logger.warning(f"Failed to auto-clear robot-side cancellation at session end: {e}")
    async def execute_plan_string(self, json_string: str, original_user_query: str = None) -> Dict[str, Any]:
        """
        从JSON字符串执行计划
        Execute plan from JSON string
        
        Args:
            json_string: JSON格式的计划字符串
                        JSON format plan string
            original_user_query: 用户的原始问题，用于have_a_look指令的上下文
        
        Returns:
            Dict[str, Any]: 执行结果 Execution result
        """
        try:
            # 解析JSON字符串
            plan_data = json.loads(json_string)
            return await self.execute_plan(plan_data, original_user_query)
        except json.JSONDecodeError as e:
            return {
                'success': False,
                'error': f"Invalid JSON format: {str(e)}"
            }
        except Exception as e:
            return {
                'success': False,
                'error': f"Exception parsing JSON: {str(e)}"
            }

    def _split_parameters(self, params_str: str) -> List[str]:
        """分割参数字符串，处理引号内的逗号"""
        params = []
        current_param = ""
        in_quotes = False
        quote_char = None
        
        for char in params_str:
            if char in ['"', "'"] and not in_quotes:
                in_quotes = True
                quote_char = char
                current_param += char
            elif char == quote_char and in_quotes:
                in_quotes = False
                quote_char = None
                current_param += char
            elif char == ',' and not in_quotes:
                if current_param.strip():
                    params.append(current_param.strip())
                current_param = ""
            else:
                current_param += char
        
        if current_param.strip():
            params.append(current_param.strip())
        
        return params
    
    def _format_execution_context(self, results: List[Dict[str, Any]]) -> str:
        """
        将执行结果格式化为字符串，用于传递给HTTP请求
        
        Args:
            results: 当前已执行的命令结果列表
            
        Returns:
            str: 格式化后的执行情况字符串
        """
        try:
            context_parts = []
            for result in results:
                command_info = f"Command {result['id']}: {result['command']}"
                execution_result = result['result']
                success_status = "SUCCESS" if execution_result.get('success', False) else "FAILED"
                
                context_part = f"{command_info} - {success_status}"
                
                # 包含完整的执行结果数据
                if not execution_result.get('success', False):
                    if 'error' in execution_result:
                        context_part += f" (Error: {execution_result['error']})"
                    # 包含完整的失败结果数据
                    context_part += f"\n  完整结果: {json.dumps(execution_result, ensure_ascii=False, indent=2)}"
                else:
                    if 'message' in execution_result:
                        context_part += f" (Message: {execution_result['message']})"
                    # 包含完整的成功结果数据
                    context_part += f"\n  完整结果: {json.dumps(execution_result, ensure_ascii=False, indent=2)}"
                
                context_parts.append(context_part)
            
            return "\n".join(context_parts)
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error formatting execution context: {str(e)}")
            return f"Error formatting execution context: {str(e)}"
    
    def _execute_have_a_look(self, command_id: str, user_query: str) -> Dict[str, Any]:
        """
        执行视觉感知流程：拍照 -> 上传图片 -> 执行工作流 -> 获取新指令序列
        
        Args:
            command_id: 命令ID
            user_query: 用户想从图像中知道的信息
            
        Returns:
            Dict[str, Any]: 执行结果
        """
        try:
            if self.logger:
                self.logger.info(f"Starting have_a_look process for command_id: {command_id}, query: {user_query}")
            # 高优先级取消检查：在进入流程前立即终止
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected before have_a_look starts, aborting early")
                return {
                    'success': False,
                    'error': 'Execution interrupted by cancel event'
                }
            
            # 播放系统音频：sentence_open_eyes（阻塞式）
            if self.voice_interface:
                try:
                    # 高优先级取消检查：播放前
                    if self.cancel_event.is_set():
                        if self.logger:
                            self.logger.warning("Cancel flag detected before playing sentence_open_eyes, skipping")
                        return {
                            'success': False,
                            'error': 'Execution interrupted by cancel event'
                        }
                    success, message, _ = self.voice_interface.play_system_sound("sentence_open_eyes", blocking=True)
                    if self.logger:
                        if success:
                            self.logger.info("Successfully played sentence_open_eyes system sound (blocking)")
                        else:
                            self.logger.warning(f"Failed to play sentence_open_eyes system sound: {message}")
                    # 高优先级取消检查：播放后
                    if self.cancel_event.is_set():
                        try:
                            # 尝试立即停止音频播放
                            self.voice_interface.stop_audio()
                        except Exception:
                            pass
                        return {
                            'success': False,
                            'error': 'Execution interrupted by cancel event'
                        }
                except Exception as e:
                    if self.logger:
                        self.logger.warning(f"Error playing sentence_open_eyes system sound: {str(e)}")
            else:
                if self.logger:
                    self.logger.warning("Voice interface not available for playing sentence_open_eyes system sound")
            
            # 1. 拍照
            import os
            import time
            
            # 生成临时图片文件路径
            timestamp = int(time.time())
            temp_image_path = f"/tmp/camera_capture_{timestamp}.jpg"
            
            # 调用机器人拍照功能
            if not self.muto_interface:
                return {
                    'success': False,
                    'error': 'Muto controller interface not available for camera capture'
                }
            # 高优先级取消检查：拍照前
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected before camera capture, aborting")
                return {
                    'success': False,
                    'error': 'Execution interrupted by cancel event'
                }
            
            # 直接使用传统的拍照方法（移除快速拍照路径）
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected before image capture, aborting")
                return {
                    'success': False,
                    'error': 'Execution interrupted by cancel event'
                }
            capture_result = self._fallback_image_capture(temp_image_path)
            if not capture_result:
                return {
                    'success': False,
                    'error': 'Camera capture failed'
                }
            
            # 高优先级取消检查：拍照后
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected after camera capture, aborting before further processing")
                return {
                    'success': False,
                    'error': 'Execution interrupted by cancel event'
                }

            # 检查图片文件是否存在
            if not os.path.exists(temp_image_path):
                return {
                    'success': False,
                    'error': f'Camera capture file not found: {temp_image_path}'
                }
            
            if self.logger:
                self.logger.info(f"Image captured successfully: {temp_image_path}")
            # 拍照后复制一份到用户目录 ~/muto-llm/
            try:
                import shutil
                archive_dir = os.path.expanduser("~/muto-llm")
                os.makedirs(archive_dir, exist_ok=True)
                archive_path = os.path.join(archive_dir, os.path.basename(temp_image_path))
                shutil.copy2(temp_image_path, archive_path)
            except Exception as copy_err:
                if self.logger:
                    self.logger.warning(f"Failed to archive captured image: {copy_err}")
            
            try:
                if self.voice_interface:
                    try:
                        # 高优先级取消检查：播放前
                        if self.cancel_event.is_set():
                            if self.logger:
                                self.logger.warning("Cancel flag detected before playing sentence_before_vision_analysis, skipping")
                            return {
                                'success': False,
                                'error': 'Execution interrupted by cancel event'
                            }
                        success, message, _ = self.voice_interface.play_system_sound("sentence_before_vision_analysis", blocking=True)
                        if self.logger:
                            if success:
                                self.logger.info("Successfully played sentence_before_vision_analysis system sound (blocking)")
                            else:
                                self.logger.warning(f"Failed to play sentence_before_vision_analysis system sound: {message}")
                        # 高优先级取消检查：播放后
                        if self.cancel_event.is_set():
                            try:
                                self.voice_interface.stop_audio()
                            except Exception:
                                pass
                            return {
                                'success': False,
                                'error': 'Execution interrupted by cancel event'
                            }
                    except Exception as e:
                        if self.logger:
                            self.logger.warning(f"Error playing sentence_before_vision_analysis system sound: {str(e)}")
                else:
                    if self.logger:
                        self.logger.warning("Voice interface not available for playing sentence_before_vision_analysis system sound")
                # 高优先级取消检查：上传前
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected before image upload, aborting")
                    return {
                        'success': False,
                        'error': 'Execution interrupted by cancel event'
                    }
                # 2. 上传图片文件到Dify
                if self.logger:
                    self.logger.info(f"临时图片路径: {temp_image_path}")
                file_id = self._upload_image_to_dify(temp_image_path, user_query)
                if not file_id:
                    return {
                        'success': False,
                        'error': 'Failed to upload image file to Dify'
                    }
                # 高优先级取消检查：上传后
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected after image upload, aborting before workflow")
                    return {
                        'success': False,
                        'error': 'Execution interrupted by cancel event'
                    }
                
                # 3. 执行工作流获取新指令序列
                # 高优先级取消检查：工作流前
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected before running vision workflow, aborting")
                    return {
                        'success': False,
                        'error': 'Execution interrupted by cancel event'
                    }
                workflow_result = self._run_dify_workflow_for_image(file_id, user_query)
                
                if workflow_result.get('status') == 'error':
                    # 如果工作流执行失败，使用带重试机制的fallback函数
                    if self.logger:
                        self.logger.warning(f"Primary workflow failed: {workflow_result.get('message', 'Unknown error')}, trying fallback with retries")
                    # 高优先级取消检查：fallback前
                    if self.cancel_event.is_set():
                        if self.logger:
                            self.logger.warning("Cancel flag detected before running fallback vision workflow, aborting")
                        return {
                            'success': False,
                            'error': 'Execution interrupted by cancel event'
                        }
                    
                    workflow_result = self._run_dify_workflow_for_image_fallback(file_id, user_query, max_retries=3)
                    
                    if workflow_result.get('status') == 'error':
                        return {
                            'success': False,
                            'error': workflow_result.get('message', 'Workflow execution failed after retries')
                        }
                
                # 4. 解析工作流返回的指令序列
                if 'data' in workflow_result and 'outputs' in workflow_result['data']:
                    outputs = workflow_result['data']['outputs']
                    
                    # 尝试获取指令序列
                    commands = None
                    if 'commands' in outputs:
                        commands = outputs['commands']
                    else:
                        # 尝试解析文本输出，支持多种字段名
                        text_output = outputs.get('text', '') or outputs.get('output', '') or outputs.get('result', '')
                        
                        if text_output:
                            try:
                                import json
                                import re
                                
                                # 先尝试直接解析JSON
                                try:
                                    parsed_data = json.loads(text_output)
                                except json.JSONDecodeError:
                                    # 如果直接解析失败，尝试提取markdown格式的JSON代码块
                                    if '```json' in text_output:
                                        json_match = re.search(r'```json\s*\n(.+?)\n```', text_output, re.DOTALL)
                                        if json_match:
                                            text_output = json_match.group(1).strip()
                                            parsed_data = json.loads(text_output)
                                        else:
                                            # 尝试提取任何JSON格式的内容
                                            json_pattern = r'\{.*\}'
                                            json_matches = re.findall(json_pattern, text_output, re.DOTALL)
                                            if json_matches:
                                                text_output = json_matches[0]
                                                parsed_data = json.loads(text_output)
                                            else:
                                                raise json.JSONDecodeError("No JSON found", text_output, 0)
                                    else:
                                        # 尝试提取任何JSON格式的内容
                                        json_pattern = r'\{.*\}'
                                        json_matches = re.findall(json_pattern, text_output, re.DOTALL)
                                        if json_matches:
                                            text_output = json_matches[0]
                                            parsed_data = json.loads(text_output)
                                        else:
                                            raise json.JSONDecodeError("No JSON found", text_output, 0)
                                
                                # 检查解析结果的结构
                                if isinstance(parsed_data, dict):
                                    # 优先查找plan字段
                                    if 'plan' in parsed_data:
                                        commands = parsed_data['plan']
                                    # 然后查找commands字段
                                    elif 'commands' in parsed_data:
                                        commands = parsed_data['commands']
                                    # 查找actions字段
                                    if 'actions' in parsed_data:
                                        commands = parsed_data['actions']
                                    # 查找steps字段
                                    elif 'steps' in parsed_data:
                                        commands = parsed_data['steps']
                                    else:
                                        # 尝试将整个字典作为单个命令处理
                                        if 'command' in parsed_data or 'action' in parsed_data:
                                            commands = [parsed_data]
                                        else:
                                            # 如果字典包含id和command字段，可能是单个命令
                                            if 'id' in parsed_data and any(key in parsed_data for key in ['command', 'action', 'type']):
                                                commands = [parsed_data]
                                elif isinstance(parsed_data, list):
                                    commands = parsed_data
                                        
                            except json.JSONDecodeError as e:
                                if self.logger:
                                    self.logger.warning(f"Could not parse commands from workflow output: {str(e)}")
                                    # 尝试从文本中提取简单的命令信息
                                    if any(keyword in text_output.lower() for keyword in ['move', 'turn', 'forward', 'backward', 'left', 'right', 'stop']):
                                        self.logger.info("Detected movement keywords in text, attempting to create simple command")
                            except Exception as e:
                                if self.logger:
                                    self.logger.error(f"Error processing workflow output: {str(e)}")
                    
                    # 如果获取到了新指令序列，标记为需要递归执行
                    if commands:
                        if self.logger:
                            self.logger.info(f"Visual analysis found {len(commands)} commands to execute")
                        return {
                            'success': True,
                            'message': f'Visual analysis completed for query: {user_query}',
                            'fetch_next_commands': True,
                            'next_commands': commands
                        }
                    else:
                        return {
                            'success': True,
                            'message': f'Visual analysis completed, but no follow-up commands generated for query: {user_query}'
                        }
                else:
                    return {
                        'success': False,
                        'error': 'Invalid workflow response format for image analysis'
                    }
                    
            finally:
                # 清理临时图片文件
                try:
                    if os.path.exists(temp_image_path):
                        os.unlink(temp_image_path)
                except:
                    pass
            
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error in have_a_look process: {str(e)}")
            return {
                'success': False,
                'error': f'Exception in have_a_look: {str(e)}'
            }
    
    def _fallback_image_capture(self, temp_image_path: str) -> bool:
        """
        传统的图像捕获回退方法
        
        Args:
            temp_image_path: 临时图像保存路径
            
        Returns:
            bool: 捕获成功返回True，失败返回False
        """
        try:
            # 检查是否有拍照方法，使用更长的超时时间和不检查相机状态的策略
            # 这样可以避免与之前的capture_image调用产生资源冲突
            if hasattr(self.muto_interface, 'capture_image'):
                capture_result = self.muto_interface.capture_image(temp_image_path)
            elif hasattr(self.muto_interface, 'take_photo'):
                capture_result = self.muto_interface.take_photo(temp_image_path)
            elif hasattr(self.muto_interface, 'camera_capture'):
                capture_result = self.muto_interface.camera_capture(temp_image_path)
            else:
                if self.logger:
                    self.logger.error('No camera capture method available in muto_interface')
                return False
            
            # 检查拍照结果
            if isinstance(capture_result, tuple) and len(capture_result) >= 2:
                if not capture_result[0]:  # 拍照失败
                    if self.logger:
                        self.logger.error(f'Fallback camera capture failed: {capture_result[1]}')
                    return False
                else:
                    if self.logger:
                        self.logger.info(f'Fallback camera capture successful: {capture_result[1]}')
                    return True
            else:
                # 如果返回值不是tuple，假设成功
                if self.logger:
                    self.logger.info('Fallback camera capture completed (non-tuple result)')
                return True
                
        except Exception as e:
            if self.logger:
                self.logger.error(f'Exception in fallback image capture: {str(e)}')
            return False

    def _upload_image_to_dify(self, image_path: str, user_query: str, user: str = "difyuser") -> str:
        """
        上传图片文件到Dify平台用于视觉分析
        
        Args:
            image_path: 图片文件路径
            user_query: 用户查询内容
            user: 用户标识
            
        Returns:
            str: 文件ID，失败返回None
        """
        try:
            import os
            
            if not os.path.exists(image_path):
                if self.logger:
                    self.logger.error(f"Image file not found: {image_path}")
                return None
            
            # 高优先级取消检查：上传前
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected before image upload to Dify, aborting upload")
                return None

            if self.logger:
                self.logger.info(f"Starting image upload to Dify: {image_path}")
            
            # 直接使用requests方法上传文件（原fallback方法）
            # Dify文件上传API配置
            upload_url = f"{DIFY_BASE_URL}/v1/files/upload"
            headers = {
                "Authorization": f"Bearer {DIFY_API_KEY}",
            }
            
            if self.logger:
                self.logger.info(f"Uploading to URL: {upload_url}")
            
            # 准备文件上传数据
            with open(image_path, 'rb') as f:
                files = {
                    'file': (os.path.basename(image_path), f, 'image/jpeg')
                }
                data = {
                    'user': user,
                    'type': 'IMAGE'
                }
                
                if self.logger:
                    self.logger.info(f"Sending POST request with data: {data}")
                # 高优先级取消检查：网络请求前
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected before image upload POST, aborting")
                    return None
                
                response = requests.post(upload_url, headers=headers, files=files, data=data, timeout=30)
            
            if self.logger:
                self.logger.info(f"Upload response status: {response.status_code}")
            
            # 高优先级取消检查：网络请求后
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected after image upload POST, ignoring response")
                return None

            if response.status_code == 201:
                result = response.json()
                file_id = result.get('id')
                if self.logger:
                    self.logger.info(f"Image uploaded successfully, file_id: {file_id}")
                    self.logger.info(f"Full upload response: {result}")
                return file_id
            else:
                if self.logger:
                    self.logger.error(f"Failed to upload image: {response.status_code} - {response.text}")
                return None
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error uploading image to Dify: {str(e)}")
            return None
    

    
    def _upload_file_to_dify(self, file_path: str, user: str = "difyuser") -> str:
        """
        上传文件到Dify平台
        
        Args:
            file_path: 要上传的文件路径
            user: 用户标识
            
        Returns:
            str: 上传成功返回文件ID，失败返回None
        """
        upload_url = f"{DIFY_BASE_URL}/v1/files/upload"
        headers = {
            "Authorization": f"Bearer {DIFY_API_KEY}",
        }
        
        try:
            # 高优先级取消检查：上传前
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected before file upload to Dify, aborting upload")
                return None
            if self.logger:
                self.logger.info(f"Uploading file: {file_path}")
            
            with open(file_path, 'rb') as file:
                files = {
                    'file': (os.path.basename(file_path), file, 'text/plain')
                }
                data = {
                    "user": user,
                    "type": "TXT"
                }
                
                # 高优先级取消检查：网络请求前
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected before file upload POST, aborting")
                    return None
                response = requests.post(upload_url, headers=headers, files=files, data=data, timeout=30)
                
                if self.logger:
                    self.logger.info(f"File upload response status: {response.status_code}")
                    if response.status_code != 201:
                        self.logger.error(f"File upload response text: {response.text}")
                
                # 高优先级取消检查：网络请求后
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected after file upload POST, ignoring response")
                    return None
                if response.status_code == 201:
                    result = response.json()
                    file_id = result.get("id")
                    if self.logger:
                        self.logger.info(f"File uploaded successfully, file_id: {file_id}")
                    return file_id
                else:
                    if self.logger:
                        self.logger.error(f"File upload failed, status code: {response.status_code}")
                        self.logger.error(f"Response content: {response.text}")
                    return None
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error uploading file: {str(e)}")
            return None
    
    def _run_dify_workflow_for_image(self, file_id: str, user_query: str, user: str = "difyuser") -> Dict[str, Any]:
        """
        运行Dify工作流进行图像分析和指令生成
        
        Args:
            file_id: 上传的图片文件ID
            user_query: 用户想从图像中知道的信息
            user: 用户标识
            
        Returns:
            Dict[str, Any]: 工作流执行结果
        """
        try:
            if self.logger:
                self.logger.info(f"Running Dify workflow for image analysis with file_id: {file_id}")
                self.logger.info("Using fallback method directly for better stability")
            # 高优先级取消检查：工作流前
            if self.cancel_event.is_set():
                if self.logger:
                    self.logger.warning("Cancel flag detected before running image workflow, aborting")
                return {"status": "error", "message": "Execution interrupted by cancel event"}
            
            # 直接使用fallback方法，避免workflow_client的不稳定性
            return self._run_dify_workflow_for_image_fallback(file_id, user_query, user)
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error running image workflow: {str(e)}")
            return {"status": "error", "message": str(e)}
    
    def _run_dify_workflow_for_image_fallback(self, file_id: str, user_query: str, user: str = "difyuser", max_retries: int = 3) -> Dict[str, Any]:
        """
        使用原始requests方法运行工作流的回退实现，支持超时重试
        """

        workflow_url = f"{DIFY_BASE_URL}/v1/workflows/run"
        headers = {
            "Authorization": f"Bearer {DIFY_API_KEY}",
            "Content-Type": "application/json"
        }

        data = {
            "inputs": {
                "input_text": user_query,
                "image_file": {
                    "type": "image",
                    "transfer_method": "local_file",
                    "upload_file_id": file_id
                },
                "cmd": "SEE_AND_CONTINUE"
            },
            "response_mode": "blocking",
            "user": user
        }

        for attempt in range(max_retries):
            try:
                # 高优先级取消检查：每次尝试开始前
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected before workflow attempt, aborting")
                    return {"status": "error", "message": "Execution interrupted by cancel event"}
                if self.logger:
                    self.logger.info(f"Running Dify workflow using fallback method (attempt {attempt + 1}/{max_retries}) with file_id: {file_id}")
                    self.logger.info(f"Workflow URL: {workflow_url}")
                    self.logger.info(f"Request data: {data}")
                
                # 高优先级取消检查：网络请求前
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected before workflow POST, aborting")
                    return {"status": "error", "message": "Execution interrupted by cancel event"}
                response = requests.post(workflow_url, headers=headers, json=data, timeout=60)
                
                if self.logger:
                    self.logger.info(f"Workflow response status: {response.status_code}")
                
                # 高优先级取消检查：网络请求后
                if self.cancel_event.is_set():
                    if self.logger:
                        self.logger.warning("Cancel flag detected after workflow POST, ignoring response")
                    return {"status": "error", "message": "Execution interrupted by cancel event"}
                if response.status_code == 200:
                    result = response.json()
                    if self.logger:
                        self.logger.info("Image analysis workflow executed successfully using fallback")
                        self.logger.info(f"Workflow result status: {result.get('status', 'unknown')}")
                        if 'data' in result:
                            self.logger.info(f"Workflow result data keys: {list(result['data'].keys()) if isinstance(result['data'], dict) else 'non-dict data'}")
                            if 'outputs' in result['data']:
                                self.logger.info(f"Workflow outputs: {result['data']['outputs']}")
                        self.logger.info(f"Complete workflow result: {result}")
                    return result
                else:
                    if self.logger:
                        self.logger.error(f"Fallback workflow execution failed, status code: {response.status_code}")
                        self.logger.error(f"Response text: {response.text}")
                    
                    # 如果不是最后一次尝试，继续重试
                    if attempt < max_retries - 1:
                        if self.logger:
                            self.logger.warning(f"Retrying workflow execution in 2 seconds...")
                        # 播放重试提示音
                        if not self.cancel_event.is_set():
                            try:
                                self.play_voice(VoiceType.SYSTEM_SOUND, "sentence_wifi_try_again")
                            except Exception as voice_error:
                                if self.logger:
                                    self.logger.warning(f"Failed to play retry sound: {voice_error}")
                        
                        import time
                        time.sleep(2)
                        continue
                    else:
                        return {"status": "error", "message": f"Failed to execute workflow using fallback after {max_retries} attempts, final status code: {response.status_code}"}
                        
            except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as timeout_error:
                if self.logger:
                    self.logger.error(f"Network timeout/connection error in fallback workflow method (attempt {attempt + 1}/{max_retries}): {str(timeout_error)}")
                
                # 如果不是最后一次尝试，播放提示音并重试
                if attempt < max_retries - 1:
                    if self.logger:
                        self.logger.warning(f"Network issue detected, playing retry sound and retrying in 3 seconds...")
                    
                    # 播放重试提示音
                    try:
                        self.play_voice(VoiceType.SYSTEM_SOUND, "sentence_wifi_try_again")
                    except Exception as voice_error:
                        if self.logger:
                            self.logger.warning(f"Failed to play retry sound: {voice_error}")
                    
                    import time
                    time.sleep(3)  # 网络问题时等待更长时间
                    continue
                else:
                    return {"status": "error", "message": f"Network timeout/connection error after {max_retries} attempts: {str(timeout_error)}"}
                    
            except Exception as e:
                if self.logger:
                    self.logger.error(f"Error in fallback workflow method (attempt {attempt + 1}/{max_retries}): {str(e)}")
                
                # 如果不是最后一次尝试且是网络相关错误，重试
                if attempt < max_retries - 1 and ("connection" in str(e).lower() or "timeout" in str(e).lower() or "network" in str(e).lower()):
                    if self.logger:
                        self.logger.warning(f"Network-related error detected, playing retry sound and retrying in 3 seconds...")
                    
                    # 播放重试提示音
                    try:
                        self.play_voice(VoiceType.SYSTEM_SOUND, "sentence_wifi_try_again")
                    except Exception as voice_error:
                        if self.logger:
                            self.logger.warning(f"Failed to play retry sound: {voice_error}")
                    
                    import time
                    time.sleep(3)
                    continue
                else:
                    return {"status": "error", "message": f"Error after {max_retries} attempts: {str(e)}"}
        
        # 理论上不会到达这里
        return {"status": "error", "message": f"Workflow execution failed after {max_retries} attempts"}
    
    def _run_dify_workflow(self, file_id: str, user: str = "difyuser", response_mode: str = "blocking") -> Dict[str, Any]:
        """
        运行Dify工作流
        
        Args:
            file_id: 上传的文件ID
            user: 用户标识
            response_mode: 响应模式
            
        Returns:
            Dict[str, Any]: 工作流执行结果
        """
        workflow_url = f"{DIFY_BASE_URL}/v1/workflows/run"
        headers = {
            "Authorization": f"Bearer {DIFY_API_KEY}",
            "Content-Type": "application/json"
        }

        data = {
            "inputs": {
                "input_text": "",
                "image_file": [{
                    "transfer_method": "local_file",
                    "upload_file_id": file_id,
                    "type": "document"
                }],
                "cmd":"SEE_AND_CONTINUE"
            },
            "response_mode": response_mode,
            "user": user
        }

        try:
            if self.logger:
                self.logger.info("Running Dify workflow...")
            
            response = requests.post(workflow_url, headers=headers, json=data, timeout=60)
            if response.status_code == 200:
                if self.logger:
                    self.logger.info("Workflow executed successfully")
                return response.json()
            else:
                if self.logger:
                    self.logger.error(f"Workflow execution failed, status code: {response.status_code}")
                return {"status": "error", "message": f"Failed to execute workflow, status code: {response.status_code}"}
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error running workflow: {str(e)}")
            return {"status": "error", "message": str(e)}

    @memory_protected(func_name="fetch_next_commands", max_memory_percent=95.0)
    async def fetch_next_commands(self, command_id: str, execution_context: str = None) -> Dict[str, Any]:
        """
        发送HTTP请求获取新的指令序列
        
        Args:
            command_id: 触发请求的命令ID
            execution_context: 前面序列的执行情况字符串
            
        Returns:
            Dict[str, Any]: 包含新指令序列的响应数据
        """
        try:
            if self.logger:
                self.logger.info(f"Fetching next commands for command_id: {command_id}")
            
            # 创建临时文件保存执行上下文
            import tempfile
            import os
            
            with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as temp_file:
                # 写入执行上下文和命令ID信息
                content = f"Command ID: {command_id}\n\n"
                if execution_context:
                    content += f"Execution Context:\n{execution_context}\n\n"
                content += "Please analyze the above execution context and provide next command sequence in JSON format."
                
                temp_file.write(content)
                temp_file_path = temp_file.name
            
            try:
                # 上传文件到Dify
                file_id = self._upload_file_to_dify(temp_file_path)
                if not file_id:
                    return {
                        'success': False,
                        'error': 'Failed to upload execution context file'
                    }
                
                # 运行工作流获取新指令序列
                workflow_result = self._run_dify_workflow(file_id)
                
                if workflow_result.get('status') == 'error':
                    return {
                        'success': False,
                        'error': workflow_result.get('message', 'Workflow execution failed')
                    }
                
                # 解析工作流返回的指令序列
                # 这里需要根据实际的Dify工作流返回格式进行调整
                if 'data' in workflow_result and 'outputs' in workflow_result['data']:
                    outputs = workflow_result['data']['outputs']
                    # 假设工作流返回的指令序列在某个字段中，需要根据实际情况调整
                    if 'commands' in outputs:
                        return {
                            'success': True,
                            'commands': outputs['commands']
                        }
                    else:
                        # 如果没有直接的commands字段，尝试解析文本输出
                        text_output = outputs.get('text', '')
                        try:
                            import json
                            commands = json.loads(text_output)
                            return {
                                'success': True,
                                'commands': commands
                            }
                        except json.JSONDecodeError:
                            return {
                                'success': False,
                                'error': f'Failed to parse command sequence from workflow output: {text_output}'
                            }
                else:
                    return {
                        'success': False,
                        'error': 'Invalid workflow response format'
                    }
                    
            finally:
                # 清理临时文件
                try:
                    os.unlink(temp_file_path)
                except:
                    pass
            
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error fetching next commands: {str(e)}")
            return {
                'success': False,
                'error': f'Exception in fetch_next_commands: {str(e)}'
            }
    
    def _convert_parameter_value(self, value: str):
        """转换参数值的类型"""
        # 尝试转换为数字类型
        try:
            if '.' in value:
                return float(value)
            else:
                return int(value)
        except ValueError:
            # 如果不是数字，保持为字符串
            return value
    

    
    async def synthesize_speech(self, text: str, output_filename: str = None, 
                               volume: float = None, max_retries: int = 3, 
                               save_file: bool = True) -> Dict[str, Any]:
        """文本转语音并保存文件"""
        import time
        import os
        
        try:
            if not self.voice_interface:
                return {
                    'success': False,
                    'message': 'Voice interface not available',
                    'text': text
                }
            
            # 检查TTS客户端是否可用
            if not hasattr(self.voice_interface, 'tts_client') or not self.voice_interface.tts_client:
                return {
                    'success': False,
                    'message': 'TTS client not available',
                    'text': text
                }
            
            start_time = time.time()
            
            # 如果没有指定输出文件名，生成一个
            if not output_filename:
                timestamp = int(time.time())
                output_filename = f"tts_output_{timestamp}.wav"
            
            # 确保文件名以.wav结尾
            if not output_filename.endswith('.wav'):
                output_filename += '.wav'
            
            # 构建完整的输出路径
            # 使用voice_module的输出目录
            output_dir = os.path.join(
                os.path.dirname(self.voice_interface.config_path),
                '..',
                'output'
            )
            os.makedirs(output_dir, exist_ok=True)
            output_path = os.path.join(output_dir, output_filename)
            
            # 调用TTS合成
            success, message, audio_data = self.voice_interface.tts_client.synthesize_text(
                text=text,
                output_file=output_path if save_file else None,
                max_retries=max_retries
            )
            
            synthesis_time = time.time() - start_time
            
            result = {
                'success': success,
                'message': message,
                'text': text,
                'synthesis_time': round(synthesis_time, 2)
            }
            
            if success and save_file and os.path.exists(output_path):
                # 获取文件信息
                file_size = os.path.getsize(output_path)
                result.update({
                    'output_file': output_path,
                    'file_size': file_size
                })
                
                # 尝试获取音频时长（如果可能）
                try:
                    import wave
                    with wave.open(output_path, 'rb') as wav_file:
                        frames = wav_file.getnframes()
                        sample_rate = wav_file.getframerate()
                        duration = frames / float(sample_rate)
                        result['audio_duration'] = round(duration, 2)
                except Exception:
                    # 如果无法获取时长，忽略错误
                    pass
            
            return result
            
        except Exception as e:
            return {
                'success': False,
                'message': f'Error in text to speech synthesis: {str(e)}',
                'text': text
            }
    
    def play_voice(self, voice_type: VoiceType, content: str, volume: float = None) -> Dict[str, Any]:
        """播放指定语音"""
        try:
            # 高优先级取消检查：若已被打断，直接跳过播放
            if hasattr(self, 'cancel_event') and self.cancel_event.is_set():
                try:
                    if self.voice_interface:
                        self.voice_interface.interrupt_audio_playback()
                        # 立刻停止当前播放（如果有），确保被打断时立即停止
                        try:
                            self.voice_interface.stop_audio(force=True)
                        except Exception:
                            pass
                except Exception:
                    pass
                return {
                    'success': False,
                    'message': 'Playback canceled',
                    'voice_type': voice_type.value,
                    'content': content
                }
            if not self.voice_interface:
                return {
                    'success': False,
                    'message': 'Voice interface not available',
                    'voice_type': voice_type.value,
                    'content': content
                }
            
            success = False
            message = ""
            
            if voice_type == VoiceType.SYSTEM_SOUND:
                # 播放系统提示音
                success, message, _ = self.voice_interface.play_system_sound(content)
                
            elif voice_type == VoiceType.COMMON_RESPONSE:
                # 播放常用系统响应声音（根据序号）
                sound_map = {
                    "1": "success",
                    "2": "error", 
                    "3": "beep",
                    "4": "wake_up",
                    "5": "start_record",
                    "6": "stop_record"
                }
                
                sound_name = sound_map.get(content)
                if sound_name:
                    success, message, _ = self.voice_interface.play_system_sound(sound_name)
                else:
                    success = False
                    message = f"Invalid common response sound number: {content}"
                    
            elif voice_type == VoiceType.CUSTOM_TEXT:
                # 文字转语音并播放
                # 再次取消检查，避免长文本合成期间未被及时打断
                if hasattr(self, 'cancel_event') and self.cancel_event.is_set():
                    try:
                        if self.voice_interface:
                            self.voice_interface.interrupt_audio_playback()
                            try:
                                self.voice_interface.stop_audio(force=True)
                            except Exception:
                                pass
                    except Exception:
                        pass
                    return {
                        'success': False,
                        'message': 'Playback canceled',
                        'voice_type': voice_type.value,
                        'content': content
                    }
                success, message, _ = self.voice_interface.play_synthesized_speech(
                    content, 
                    volume=volume
                )
            
            return {
                'success': success,
                'message': message,
                'voice_type': voice_type.value,
                'content': content
            }
            
        except Exception as e:
            return {
                'success': False,
                'message': f'Error playing voice: {str(e)}',
                'voice_type': voice_type.value,
                'content': content
            }
    
    def _generate_command_signature(self, plan_list: List[Dict[str, Any]]) -> str:
        """生成命令序列的签名，用于检测重复序列"""
        try:
            # 提取关键信息：命令名称和主要参数
            signature_parts = []
            for item in plan_list:
                command = item.get('command', '')
                # 对于某些命令，包含关键参数以提高检测精度
                if command in ['have_a_look', 'wait_for_next_step']:
                    params = item.get('parameters', {})
                    if isinstance(params, dict):
                        # 只包含关键参数，避免签名过长
                        key_params = {k: v for k, v in params.items() if k in ['user_query', 'original_request']}
                        signature_parts.append(f"{command}:{str(key_params)}")
                    else:
                        signature_parts.append(command)
                else:
                    signature_parts.append(command)
            
            return "|".join(signature_parts)
        except Exception as e:
            if self.logger:
                self.logger.warning(f"Error generating command signature: {e}")
            return "unknown_signature"
    
    def _is_duplicate_sequence(self, command_signature: str) -> bool:
        """检查是否存在重复的命令序列"""
        try:
            # 统计当前签名在历史中出现的次数
            count = self.command_sequence_history.count(command_signature)
            return count >= self.duplicate_sequence_threshold
        except Exception as e:
            if self.logger:
                self.logger.warning(f"Error checking duplicate sequence: {e}")
            return False
    
    def _record_command_sequence(self, command_signature: str):
        """记录命令序列到历史中"""
        try:
            self.command_sequence_history.append(command_signature)
            
            # 保持历史记录在合理范围内
            if len(self.command_sequence_history) > self.max_sequence_history:
                # 移除最旧的记录
                self.command_sequence_history.pop(0)
                
        except Exception as e:
            if self.logger:
                self.logger.warning(f"Error recording command sequence: {e}")
    
    def _execute_wait_for_next_step(self, command_id: str, original_request: str, executed_commands: str, next_planning: str, execution_results: List[Dict[str, Any]] = None) -> Dict[str, Any]:
        """
        执行等待下一步规划流程：基于前序操作结果调用API重新规划下一步指令序列
        
        Args:
            command_id: 命令ID
            original_request: 用户的原始指令需求
            executed_commands: 前面已经运行了的指令和运行的结果（简要描述）
            next_planning: 接下来需要怎么规划
            execution_results: 完整的执行结果列表（可选，如果提供则会覆盖executed_commands）
            
        Returns:
            Dict[str, Any]: 执行结果
        """
        try:
            # 记录上下文参数，便于调试
            if self.logger:
                try:
                    src_context = 'execution_results' if execution_results else 'executed_commands'
                    self.logger.info(
                        f"wait_for_next_step ctx: original_request={repr(original_request)}, "
                        f"next_planning={repr(next_planning)}, context_source={src_context}"
                    )
                except Exception:
                    pass
            # 播放系统音频：sentence_before_wait_for_next_step（阻塞式）
            # if self.voice_interface:
            #     try:
            #         if self.cancel_event.is_set():
            #             if self.logger:
            #                 self.logger.warning("Cancel flag detected before playing sentence_before_wait_for_next_step, skipping")
            #             return {
            #                 'success': False,
            #                 'error': 'Execution interrupted by cancel event'
            #             }
            #         success, message, _ = self.voice_interface.play_system_sound("sentence_before_wait_for_next_step", blocking=True)
            #         if self.logger:
            #             if success:
            #                 self.logger.info("Successfully played sentence_before_wait_for_next_step system sound (blocking)")
            #             else:
            #                 self.logger.warning(f"Failed to play sentence_before_wait_for_next_step system sound: {message}")
            #         if self.cancel_event.is_set():
            #             try:
            #                 if self.voice_interface:
            #                     self.voice_interface.stop_audio(force=True)
            #             except Exception:
            #                 pass
            #             return {
            #                 'success': False,
            #                 'error': 'Execution interrupted by cancel event'
            #             }
            #     except Exception as e:
            #         if self.logger:
            #             self.logger.warning(f"Error playing sentence_before_wait_for_next_step system sound: {str(e)}")
            # else:
            #     if self.logger:
            #         self.logger.warning("Voice interface not available for playing sentence_before_wait_for_next_step system sound")
            
            # 如果提供了完整的执行结果，使用_format_execution_context格式化
            if execution_results:
                formatted_execution_context = self._format_execution_context(execution_results)
            else:
                formatted_execution_context = executed_commands
            
            # 构建简短的查询内容用于input_text（限制256字符）
            short_query = f"基于执行历史进行下一步规划"
            
            # 构建详细的查询内容，通过文件上传
            detailed_query_content = f"""
原始用户需求: {original_request}

已执行的指令和结果: {formatted_execution_context}

下一步规划需求: {next_planning}

请基于以上信息，分析当前状态并生成下一步的指令序列。
"""
            
            try:
                # 创建临时文件保存详细查询内容
                import tempfile
                import os
                with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as temp_file:
                    temp_file.write(detailed_query_content)
                    temp_file_path = temp_file.name
                
                try:
                    # 上传文件到Dify
                    file_id = self._upload_file_to_dify(temp_file_path)
                    if not file_id:
                        return {
                            'success': False,
                            'error': 'Failed to upload query context file to Dify'
                        }
                    
                    # 取消检查
                    if self.cancel_event.is_set():
                        return {'success': False, 'error': 'Execution interrupted'}
                    # 执行工作流获取新指令序列（使用文件上传方式）
                    workflow_result = self._run_dify_workflow_for_next_plan_with_file(short_query, file_id)
                    # 详细打印workflow_result用于调试
                    try:
                        # 清理临时文件
                        os.unlink(temp_file_path)
                    except:
                        pass
                except:
                    pass
                
                if workflow_result.get('status') == 'error':
                    return {
                        'success': False,
                        'error': workflow_result.get('message', 'Workflow execution failed')
                    }
                
                # 解析工作流返回的指令序列
                commands = None
                if 'data' in workflow_result and 'outputs' in workflow_result['data']:
                    outputs = workflow_result['data']['outputs']
                    
                    # 尝试获取指令序列
                    if 'commands' in outputs:
                        commands = outputs['commands']
                    else:
                        # 尝试解析文本输出，支持多种字段名
                        text_output = outputs.get('text', '') or outputs.get('output', '') or outputs.get('result', '')
                        
                        if text_output:
                            try:
                                import json
                                import re
                                
                                # 先尝试直接解析JSON
                                try:
                                    parsed_data = json.loads(text_output)
                                except json.JSONDecodeError:
                                    # 如果直接解析失败，尝试提取markdown格式的JSON代码块
                                    if '```json' in text_output:
                                        json_match = re.search(r'```json\s*\n(.+?)\n```', text_output, re.DOTALL)
                                        if json_match:
                                            text_output = json_match.group(1).strip()
                                            parsed_data = json.loads(text_output)
                                        else:
                                            # 尝试提取任何JSON格式的内容
                                            json_pattern = r'\{.*\}'
                                            json_matches = re.findall(json_pattern, text_output, re.DOTALL)
                                            if json_matches:
                                                text_output = json_matches[0]
                                                parsed_data = json.loads(text_output)
                                            else:
                                                raise json.JSONDecodeError("No JSON found", text_output, 0)
                                    else:
                                        # 尝试提取任何JSON格式的内容
                                        json_pattern = r'\{.*\}'
                                        json_matches = re.findall(json_pattern, text_output, re.DOTALL)
                                        if json_matches:
                                            text_output = json_matches[0]
                                            parsed_data = json.loads(text_output)
                                        else:
                                            raise json.JSONDecodeError("No JSON found", text_output, 0)
                                
                                # 检查解析结果的结构
                                if isinstance(parsed_data, dict):
                                    # 优先查找plan字段
                                    if 'plan' in parsed_data:
                                        commands = parsed_data['plan']
                                    # 然后查找commands字段
                                    elif 'commands' in parsed_data:
                                        commands = parsed_data['commands']
                                    # 查找actions字段
                                    elif 'actions' in parsed_data:
                                        commands = parsed_data['actions']
                                    # 查找steps字段
                                    elif 'steps' in parsed_data:
                                        commands = parsed_data['steps']
                                    else:
                                        # 尝试将整个字典作为单个命令处理
                                        if 'command' in parsed_data or 'action' in parsed_data:
                                            commands = [parsed_data]
                                        else:
                                            # 如果字典包含id和command字段，可能是单个命令
                                            if 'id' in parsed_data and any(key in parsed_data for key in ['command', 'action', 'type']):
                                                commands = [parsed_data]
                                elif isinstance(parsed_data, list):
                                    commands = parsed_data
                                
                            except json.JSONDecodeError as e:
                                if self.logger:
                                    self.logger.warning(f"Could not parse commands from workflow output: {text_output[:100]}...")
                                    # 尝试从文本中提取简单的命令信息
                                    if any(keyword in text_output.lower() for keyword in ['move', 'turn', 'forward', 'backward', 'left', 'right', 'stop']):
                                        if self.logger:
                                            self.logger.info("Detected movement keywords in text, attempting to create simple command")
                                        # 这里可以添加简单的文本解析逻辑
                            except Exception as e:
                                if self.logger:
                                    self.logger.error(f"Error processing workflow output: {str(e)}")
                    
                    # 如果成功获取到指令序列，返回成功结果
                    if commands and isinstance(commands, list) and len(commands) > 0:
                        if self.logger:
                            self.logger.info(f"Successfully parsed {len(commands)} commands from wait_for_next_step workflow")
                        
                        return {
                            'success': True,
                            'message': f'Next step planning completed successfully with {len(commands)} commands',
                            'commands': commands,
                            'next_commands': commands,  # 添加next_commands字段以触发后续执行
                            'fetch_next_commands': True,  # 添加fetch_next_commands标志
                            'command_id': command_id,
                            'original_request': original_request,
                            'executed_commands': executed_commands,
                            'next_planning': next_planning
                        }
                    else:
                        if self.logger:
                            self.logger.warning(f"No valid commands found in workflow response for wait_for_next_step")
                        return {
                            'success': True,
                            'message': f'Next step planning completed, but no follow-up commands generated for query: {short_query}'
                        }
                else:
                    return {
                        'success': False,
                        'error': 'Invalid workflow response format for next step planning'
                    }
                    
            except Exception as e:
                if self.logger:
                    self.logger.error(f"Error in wait_for_next_step workflow execution: {str(e)}")
                return {
                    'success': False,
                    'error': f'Exception in wait_for_next_step workflow: {str(e)}'
                }
            
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error in wait_for_next_step process: {str(e)}")
            return {
                'success': False,
                'error': f'Exception in wait_for_next_step: {str(e)}'
            }
    
    def _run_dify_workflow_for_next_plan_with_file(self, short_query: str, file_id: str, user: str = "difyuser") -> Dict[str, Any]:
        """
        运行Dify工作流进行下一步规划（使用文件上传方式）
        
        Args:
            short_query: 简短的查询描述（用于input_text，限制256字符）
            file_id: 上传的详细查询文件ID
            user: 用户标识
            
        Returns:
            Dict[str, Any]: 工作流执行结果
        """
        try:
            # 取消检查
            if self.cancel_event.is_set():
                return {"status": "error", "message": "Canceled"}
            if self.logger:
                self.logger.info(f"Running Dify workflow for next step planning with file upload")
            
            workflow_url = f"{DIFY_BASE_URL}/v1/workflows/run"
            headers = {
                "Authorization": f"Bearer {DIFY_API_KEY}",
                "Content-Type": "application/json"
            }

            data = {
                "inputs": {
                    "input_text": short_query,
                    "process_log": {
                        "type": "document",
                        "transfer_method": "local_file",
                        "upload_file_id": file_id
                    },
                    "cmd": "WAIT_FOR_NEXT_STEP"
                },
                "response_mode": "blocking",
                "user": user
            }

            if self.logger:
                self.logger.info(f"Workflow URL: {workflow_url}")
                self.logger.info(f"Request data: {data}")
            if self.cancel_event.is_set():
                return {"status": "error", "message": "Canceled"}
            
            response = requests.post(workflow_url, headers=headers, json=data, timeout=60)
            
            if self.logger:
                self.logger.info(f"Workflow response status: {response.status_code}")
                try:
                    self.logger.info(f"Workflow response headers: {dict(response.headers)}")
                except Exception:
                    pass
                resp_text = None
                try:
                    resp_text = response.text
                    self.logger.info(f"Workflow response text length: {len(resp_text)}")
                    self.logger.info(f"Workflow response text: {resp_text}")
                except Exception as e:
                    self.logger.warning(f"Failed to read response text: {str(e)}")
            
            if response.status_code == 200:
                result = None
                try:
                    result = response.json()
                    if self.logger:
                        self.logger.info(f"Parsed JSON successfully. Top-level keys: {list(result.keys())}")
                        # 深入打印data/outputs
                        if isinstance(result, dict) and 'data' in result:
                            data_section = result['data']
                            try:
                                self.logger.info(f"Data section keys: {list(data_section.keys())}")
                            except Exception:
                                pass
                            # 打印outputs
                            if isinstance(data_section, dict) and 'outputs' in data_section:
                                outputs_section = data_section['outputs']
                                try:
                                    self.logger.info(f"Outputs section keys: {list(outputs_section.keys())}")
                                    self.logger.info(f"Outputs section content: {outputs_section}")
                                except Exception:
                                    self.logger.info(f"Outputs section content could not be serialized directly")
                            # 打印nodes（如存在）
                            if isinstance(data_section, dict) and 'nodes' in data_section:
                                nodes_section = data_section['nodes']
                                try:
                                    self.logger.info(f"Nodes section type: {type(nodes_section)}")
                                    self.logger.info(f"Nodes section content: {nodes_section}")
                                except Exception:
                                    self.logger.info(f"Nodes section could not be serialized directly")
                            # 打印其他可能的字段
                            for extra_key in ['response_data', 'result', 'events', 'tasks', 'variables']:
                                if isinstance(data_section, dict) and extra_key in data_section:
                                    try:
                                        self.logger.info(f"Data['{extra_key}'] type: {type(data_section[extra_key])}")
                                        self.logger.info(f"Data['{extra_key}'] content: {data_section[extra_key]}")
                                    except Exception:
                                        self.logger.info(f"Data['{extra_key}'] could not be serialized directly")
                except Exception as e:
                    if self.logger:
                        self.logger.error(f"Failed to parse JSON from response: {str(e)}")
                if self.logger:
                    self.logger.info(f"Next step planning workflow executed successfully with file upload")
                return result if result is not None else {"status": "ok", "raw_text": resp_text}
            else:
                if self.logger:
                    self.logger.error(f"Next step planning workflow execution failed with file upload, status code: {response.status_code}")
                    try:
                        self.logger.error(f"Response text: {response.text}")
                    except Exception:
                        pass
                return {
                    "status": "error",
                    "message": f"Failed to execute next step planning workflow with file upload, status code: {response.status_code}"
                }
                
        except Exception as e:
            if self.logger:
                self.logger.error(f"Error running next step planning workflow with file upload: {str(e)}")
            return {"status": "error", "message": str(e)}

    def run_text_workflow(self, input_text: str, user: str = "difyuser", timeout: int = 60) -> Dict[str, Any]:
        try:
            if self.cancel_event.is_set():
                return {"status": "error", "message": "Canceled"}
            workflow_url = f"{DIFY_BASE_URL}/v1/workflows/run"
            headers = {
                "Authorization": f"Bearer {DIFY_API_KEY}",
                "Content-Type": "application/json"
            }
            data = {
                "inputs": {"input_text": input_text},
                "response_mode": "blocking",
                "user": user
            }
            if self.logger:
                self.logger.info(f"Running text workflow: {input_text[:50]}...")
            if self.cancel_event.is_set():
                return {"status": "error", "message": "Canceled"}
            response = requests.post(workflow_url, headers=headers, json=data, timeout=timeout)
            if self.logger:
                self.logger.info(f"Text workflow status: {response.status_code}")
            if self.cancel_event.is_set():
                return {"status": "error", "message": "Canceled"}
            if response.status_code == 200:
                try:
                    return response.json()
                except Exception:
                    return {"status": "ok", "raw_text": response.text}
            else:
                return {"status": "error", "message": f"HTTP {response.status_code}"}
        except Exception as e:
            if self.logger:
                self.logger.error(f"run_text_workflow error: {str(e)}")
            return {"status": "error", "message": str(e)}

    def play_voice_blocking(self, voice_type: VoiceType, content: str, volume: float = None) -> Dict[str, Any]:
        """播放指定语音（阻塞模式）"""
        try:
            if self.cancel_event.is_set():
                try:
                    if self.voice_interface:
                        self.voice_interface.interrupt_audio_playback()
                        try:
                            self.voice_interface.stop_audio(force=True)
                        except Exception:
                            pass
                except Exception:
                    pass
                return {
                    'success': False,
                    'message': 'Playback canceled',
                    'voice_type': voice_type.value,
                    'content': content
                }
            if not self.voice_interface:
                return {
                    'success': False,
                    'message': 'Voice interface not available',
                    'voice_type': voice_type.value,
                    'content': content
                }
            
            success = False
            message = ""
            
            if voice_type == VoiceType.SYSTEM_SOUND:
                # 播放系统提示音
                success, message, _ = self.voice_interface.play_system_sound(content)
                
            elif voice_type == VoiceType.COMMON_RESPONSE:
                # 播放常用系统响应声音（根据序号）
                sound_map = {
                    "1": "success",
                    "2": "error", 
                    "3": "beep",
                    "4": "wake_up",
                    "5": "start_record",
                    "6": "stop_record"
                }
                
                sound_name = sound_map.get(content)
                if sound_name:
                    success, message, _ = self.voice_interface.play_system_sound(sound_name)
                else:
                    success = False
                    message = f"Invalid common response sound number: {content}"
                    
            elif voice_type == VoiceType.CUSTOM_TEXT:
                # 文字转语音并播放（阻塞模式）
                success, message, _ = self.voice_interface.play_synthesized_speech(
                    content, 
                    volume=volume,
                    use_queue=False  # 使用直接播放模式，实现阻塞播放
                )
            
            return {
                'success': success,
                'message': message,
                'voice_type': voice_type.value,
                'content': content
            }
            
        except Exception as e:
            return {
                'success': False,
                'message': f'Error playing voice: {str(e)}',
                'voice_type': voice_type.value,
                'content': content
            }
